// Kanrong Shan
// July 23, 2010
#ifndef __H_RING_BUFFER__
#define __H_RING_BUFFER__


class RingBuffer
{
public:
	RingBuffer()
	{
		buff_ = NULL;
		write_ptr_ = NULL;
		read_ptr_ = NULL;
		buff_size_ = 0;
		is_empty_ = true;
	}

	~RingBuffer()
	{
		delete[] buff_;
	}

	bool Create(int size)
	{
		buff_ = new unsigned char[size];
		if(buff_)
		{
			buff_size_ = size;
			is_empty_ = true;
			return true;
		}
		return false;
	}

	bool Clear()
	{
		write_ptr_ = 0;
		read_ptr_ = 0;
		is_empty_ = true;
	}

	int GetAvailableSize()
	{
		if(buff_)
		{
			if(read_ptr_ == write_ptr_)
			{
				if(is_empty_)
					return buff_size_;
				else
					return 0;
			}

			if(write_ptr_ < read_ptr_)
				return read_ptr_-write_ptr_;

			if(write_ptr_ > read_ptr_)
				return buff_size_-write_ptr_+read_ptr_;
		}

		return 0;
	}

	int GetUsedSize()
	{
		if(buff_)
		{
			if(read_ptr_ == write_ptr_)
			{
				if(is_empty_)
					return 0;
				else
					return buff_size_;
			}

			if(read_ptr_ < write_ptr_)
				return write_ptr_-read_ptr_;

			if(read_ptr_ > write_ptr_)
				return buff_size_-read_ptr_+write_ptr_;
		}

		return 0;
	}

	bool Write(const unsigned char* buff, int len)
	{
		bool ret = false;

		if(len <= GetAvailableSize())
		{
			if(write_ptr_+len <= buff_size_)	// No wrapping
			{
				CopyMemory(&buff_[write_ptr_], buff, len);
				write_ptr_ += len;
			}
			else
			{
				int firstChunk = buff_size_ - write_ptr_;
				int secondChunk = len - firstChunk;

				if(firstChunk>0)
					CopyMemory(&buff_[write_ptr_], buff, firstChunk);
				CopyMemory(&buff_[0], &buff[firstChunk], secondChunk);

				write_ptr_ = secondChunk;
			}
			if(write_ptr_ == read_ptr_)
				is_empty_ = false;
			ret = true;
		}

		return ret;
	}

	bool Read(unsigned char* buff, int len)
	{
		bool bResult = FALSE;
		{
			if( len <= GetUsedSize() )
			{
				// easy case, no wrapping
				if( read_ptr_ + len <= buff_size_ )
				{
					CopyMemory( buff, &buff_[read_ptr_], len );
					read_ptr_ += len;
				}
				else // harder case, buffer wraps
				{
					int iFirstChunkSize = buff_size_ - read_ptr_;
					int iSecondChunkSize = len - iFirstChunkSize;

					if(iFirstChunkSize>0)
						CopyMemory( buff, &buff_[read_ptr_], iFirstChunkSize );
					CopyMemory( &buff[iFirstChunkSize], &buff_[0], iSecondChunkSize );

					read_ptr_ = iSecondChunkSize;
				}
				if(write_ptr_ == read_ptr_)
					is_empty_ = true;
				bResult =TRUE;
			}
		}
		return bResult;
	}

private:
	unsigned char* buff_;
	int buff_size_;
	int write_ptr_;
	int read_ptr_;
	bool is_empty_;
};

#endif